- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2
🤖 I've added comprehensive unit tests for calendar converters and base … #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
…classes. This commit introduces extensive unit tests for newly added and existing calendar functionality: - **GregorianDateConverter:** I added a new test suite (`test_gregorian.py`) to cover all `BaseCalendarConverter` methods (`min_month`, `max_month`, `max_day`, `to_gregorian`, etc.). - **BaseDateConverter Subclass Discovery:** I added tests to `test_base.py` to verify that `available_converters()` correctly discovers direct, nested, and calendar converter subclasses, and properly excludes `BaseCalendarConverter`. This included testing the `import_converters` caching and module loading. - **HebrewDateConverter:** I enhanced `test_hebrew_converter.py` with explicit tests for all `BaseCalendarConverter` methods. I made minor adjustments to the converter to handle `None` inputs for year/month in `max_day` and `max_month`. - **IslamicDateConverter:** I enhanced `test_islamic_converter.py` with explicit tests for `BaseCalendarConverter` methods and corrected assertions in `test_partially_known`. I made minor adjustments to the converter to handle `None` inputs for year/month in `max_day`. - **SeleucidDateConverter:** I enhanced `test_seleucid.py` with explicit tests for `to_gregorian`, `max_month`, and `max_day` using Seleucid years. I overrode `max_month`, `max_day`, and `parse` in the converter to correctly handle the `SELEUCID_OFFSET` and provide Seleucid-specific error messages. I also cleaned up commented code. All new and existing tests pass, ensuring better coverage and robustness of the date conversion and calendar logic.
| Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the  You can disable this status message by setting the  WalkthroughOptional parameters and default handling were added to  Changes
 Sequence Diagram(s)sequenceDiagram
    participant Caller
    participant HebrewConverter
    participant IslamicConverter
    participant SeleucidConverter
    participant ParentConverter
    Caller->>HebrewConverter: max_month(year=None)
    alt year is None
        HebrewConverter-->>Caller: return 12
    else year provided
        HebrewConverter->>hebrew.year_months: get months in year
        hebrew.year_months-->>HebrewConverter: months
        HebrewConverter-->>Caller: return months
    end
    Caller->>HebrewConverter: max_day(year=None, month=None)
    HebrewConverter->>hebrew.month_days: with fallback year/month
    hebrew.month_days-->>HebrewConverter: days
    HebrewConverter-->>Caller: return days
    Caller->>IslamicConverter: max_day(year=None, month=None)
    IslamicConverter->>islamic.month_length: with fallback year/month
    islamic.month_length-->>IslamicConverter: days
    IslamicConverter-->>Caller: return days
    Caller->>SeleucidConverter: max_month(year)
    SeleucidConverter->>ParentConverter: max_month(year + offset)
    ParentConverter-->>SeleucidConverter: months
    SeleucidConverter-->>Caller: return months
    Caller->>SeleucidConverter: parse(value)
    SeleucidConverter->>ParentConverter: parse(value)
    alt parse fails
        SeleucidConverter-->>Caller: raise ValueError with custom message
    else parse succeeds
        SeleucidConverter-->>Caller: return parsed label
    end
Possibly related PRs
 Suggested reviewers
 Poem
 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit: 
 SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
 Other keywords and placeholders
 CodeRabbit Configuration File ( | 
| Codecov ReportAttention: Patch coverage is  
 
 Additional details and impacted files@@                  Coverage Diff                  @@
##           feature/pgp-dates     #132      +/-   ##
=====================================================
- Coverage              98.60%   98.10%   -0.51%     
=====================================================
  Files                     37       38       +1     
  Lines                   1582     1850     +268     
=====================================================
+ Hits                    1560     1815     +255     
- Misses                    22       35      +13     ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
 | 
| @coderabbitai review | 
| ✅ Actions performedReview triggered. 
 | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (3)
src/undate/converters/calendars/hebrew/converter.py (1)
49-66: Refactor for better readability and remove unnecessary logic.The implementation is functionally correct but has some issues:
- Unnecessary conditional logic: The conditional check on lines 60-63 for Adar months is redundant since you're already defaulting to a non-leap year (5783).
- Code clarity: The extensive comments, while helpful, make the code harder to read.
Consider this cleaner implementation:
- def max_day(self, year: int = None, month: int = None) -> int: # Added default None + def max_day(self, year: int = None, month: int = None) -> int: """maximum numeric day for the specified year and month in this calendar""" - # NOTE: unreleased v2.4.1 of convertdate standardizes month_days to month_length - - # Handle None year/month by defaulting to a common non-leap scenario - # Default year to a known non-leap year if None, e.g. 5783 - # Default month to Nisan (1) if None, as it's the first biblical month and always has 30 days. - effective_year = year if year is not None else 5783 # 5783 is a non-leap year - effective_month = month if month is not None else hebrew.NISAN # Nisan is 1 - - # Ensure year is not None for leap check if month is Adar related and year was originally None - if year is None and (effective_month == 12 or effective_month == 13): # Adar, Adar I or Adar II - # hebrew.month_days needs a concrete year to determine leap month lengths correctly. - # We've defaulted to 5783 (non-leap). - pass - - - return hebrew.month_days(effective_year, effective_month) + # Default to a known non-leap year and first month if parameters are None + effective_year = year if year is not None else 5783 # 5783 is a non-leap year + effective_month = month if month is not None else hebrew.NISAN + return hebrew.month_days(effective_year, effective_month)src/undate/converters/calendars/seleucid.py (2)
22-28: Apply static analysis suggestion for cleaner code.The logic is correct, but the code can be simplified using a ternary operator as suggested by the static analysis tool.
- def max_month(self, year: int = None) -> int: - """Maximum numeric month for this calendar. Adjusted for Seleucid year.""" - if year is not None: - am_year = year + self.SELEUCID_OFFSET - else: - am_year = None # Let parent handle None AM year if applicable (it defaults to non-leap) - return super().max_month(am_year) + def max_month(self, year: int = None) -> int: + """Maximum numeric month for this calendar. Adjusted for Seleucid year.""" + am_year = year + self.SELEUCID_OFFSET if year is not None else None + return super().max_month(am_year)🧰 Tools
🪛 Ruff (0.11.9)
24-27: Use ternary operator
am_year = year + self.SELEUCID_OFFSET if year is not None else Noneinstead ofif-else-block(SIM108)
30-38: Apply static analysis suggestion and improve consistency.Similar to
max_month, this can be simplified with a ternary operator.- def max_day(self, year: int = None, month: int = None) -> int: - """Maximum numeric day for the specified year and month. Adjusted for Seleucid year.""" - if year is not None: - am_year = year + self.SELEUCID_OFFSET - else: - am_year = None # Parent's max_day handles None year by defaulting - - # Parent's max_day handles None month by defaulting, so pass month as is. - return super().max_day(am_year, month) + def max_day(self, year: int = None, month: int = None) -> int: + """Maximum numeric day for the specified year and month. Adjusted for Seleucid year.""" + am_year = year + self.SELEUCID_OFFSET if year is not None else None + return super().max_day(am_year, month)🧰 Tools
🪛 Ruff (0.11.9)
32-35: Use ternary operator
am_year = year + self.SELEUCID_OFFSET if year is not None else Noneinstead ofif-else-block(SIM108)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
- src/undate/converters/calendars/hebrew/converter.py(2 hunks)
- src/undate/converters/calendars/islamic/converter.py(1 hunks)
- src/undate/converters/calendars/seleucid.py(1 hunks)
- tests/test_converters/test_base.py(1 hunks)
- tests/test_converters/test_calendars/test_gregorian.py(1 hunks)
- tests/test_converters/test_calendars/test_hebrew/test_hebrew_converter.py(2 hunks)
- tests/test_converters/test_calendars/test_islamic/test_islamic_converter.py(2 hunks)
- tests/test_converters/test_calendars/test_seleucid.py(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
tests/test_converters/test_calendars/test_gregorian.py (2)
src/undate/converters/calendars/gregorian.py (1)
GregorianDateConverter(6-51)tests/test_converters/test_base.py (4)
min_month(34-34)
max_month(35-35)
max_day(36-36)
to_gregorian(32-32)
src/undate/converters/calendars/islamic/converter.py (3)
src/undate/converters/calendars/seleucid.py (1)
max_day(30-38)src/undate/converters/calendars/hebrew/converter.py (1)
max_day(49-66)src/undate/converters/base.py (1)
max_day(161-163)
🪛 Ruff (0.11.9)
src/undate/converters/calendars/seleucid.py
24-27: Use ternary operator am_year = year + self.SELEUCID_OFFSET if year is not None else None instead of if-else-block
(SIM108)
32-35: Use ternary operator am_year = year + self.SELEUCID_OFFSET if year is not None else None instead of if-else-block
(SIM108)
tests/test_converters/test_base.py
5-5: typing.Type imported but unused
Remove unused import: typing.Type
(F401)
244-244: Local variable original_iter_modules is assigned to but never used
Remove assignment to unused variable original_iter_modules
(F841)
🪛 GitHub Actions: Check style + docs + types
tests/test_converters/test_base.py
[error] 5-5: Ruff: typing.Type imported but unused (F401). Remove unused import.
[error] 244-244: Ruff: Local variable original_iter_modules is assigned to but never used (F841). Remove assignment to unused variable.
🔇 Additional comments (17)
src/undate/converters/calendars/hebrew/converter.py (1)
31-38: LGTM! Clean implementation of optional year parameter.The
max_monthmethod correctly handles the optional year parameter by defaulting to 12 months for non-leap years when year is not specified. This aligns well with the Hebrew calendar's structure.src/undate/converters/calendars/islamic/converter.py (1)
27-35: Excellent implementation - clean and consistent.The
max_daymethod implementation is well-structured and follows good practices:
- Clear, concise logic with appropriate fallback defaults
- Good choice of 1446 AH (non-leap year) and month 1 (Muharram) for defaults
- Consistent with the pattern established across other calendar converters
- Clean documentation explaining the behavior
This implementation is cleaner than the Hebrew converter version and serves as a good example.
tests/test_converters/test_calendars/test_gregorian.py (3)
1-26: Excellent test structure and comprehensive coverage.The test class setup and basic method tests are well-implemented:
- Clear test organization with descriptive method names
- Good coverage of boundary conditions including None parameters
- Proper testing of leap year scenarios in max_month tests
28-49: Outstanding parametrized test design.The parametrized test for
max_dayprovides excellent coverage:
- Comprehensive test cases covering all scenarios (known/unknown year/month combinations)
- Proper leap year testing (2024 vs 2023, None defaults to non-leap)
- Clear, readable test data structure
- Good edge case coverage including the maximum default (31 days)
This is a model example of thorough parametrized testing.
51-62: Complete test coverage with important edge cases.The
to_gregoriantest covers crucial scenarios:
- Regular dates
- Leap day (Feb 29, 2024)
- Century leap year exceptions (1900 non-leap, 2000 leap)
This ensures the Gregorian converter correctly handles all calendar edge cases.
src/undate/converters/calendars/seleucid.py (1)
18-21: Good documentation of parser inheritance decision.The comment clearly explains the current parser strategy and what would need to change if Seleucid date formats differ from Hebrew. This helps future maintainers understand the design decision.
tests/test_converters/test_calendars/test_islamic/test_islamic_converter.py (3)
7-7: Good improvements to test structure!Adding the
convertdateimport and class-level converter instance improves test maintainability and performance.Also applies to: 11-11
92-107: Excellent improvement using dynamic month length calculations!Using
islamic.month_length()instead of hardcoded values ensures the tests remain accurate if the calendar implementation changes.
127-185: Comprehensive test coverage for BaseCalendarConverter methods!The new tests thoroughly verify:
- Min/max month boundaries
- Leap year handling
- Default behavior with None parameters
- Direct Gregorian conversions
This significantly improves test coverage and reliability.
tests/test_converters/test_calendars/test_hebrew/test_hebrew_converter.py (2)
7-7: Consistent test structure improvements!Following the same pattern as the Islamic converter tests improves consistency across the test suite.
Also applies to: 11-11
160-251: Thorough test coverage for Hebrew calendar specifics!The tests properly handle Hebrew calendar complexities:
- Leap year detection with 13 months
- Adar I/II handling in leap years
- Correct month numbering and day counts
- Edge cases with None parameters
Excellent attention to calendar-specific details.
tests/test_converters/test_calendars/test_seleucid.py (3)
1-1: Well-structured test setup!Good additions for pytest assertions and Hebrew leap year checks needed for Seleucid calendar calculations.
Also applies to: 5-5, 9-9
60-77: Excellent test coverage for Seleucid-specific conversions!The tests properly verify:
- Direct Gregorian conversions
- SELEUCID_OFFSET calculations
- Leap year handling through Hebrew calendar
Good attention to the relationship between Seleucid and Hebrew calendars.
120-135: Comprehensive error message testing!The tests verify both the error type and message content, ensuring users get helpful error messages specific to Seleucid dates.
tests/test_converters/test_base.py (3)
15-37: Well-designed dummy converters for testing!The dummy converter classes provide good test coverage for:
- Direct subclasses
- Calendar converter subclasses
- Required method implementations
This enables thorough testing of the discovery mechanism.
41-117: Excellent test coverage for converter discovery mechanisms!The tests comprehensively verify:
- Caching behavior of
import_converters- Module loading side effects
- Mock-based testing to isolate behavior
Great use of mocking to test the import mechanism without side effects.
197-314: Impressive dynamic module testing!This test for dynamic module discovery is particularly well-crafted:
- Creates a temporary module at runtime
- Tests the discovery mechanism with realistic conditions
- Properly cleans up after itself
This ensures the converter discovery works for dynamically loaded modules.
🧰 Tools
🪛 Ruff (0.11.9)
244-244: Local variable
original_iter_modulesis assigned to but never usedRemove assignment to unused variable
original_iter_modules(F841)
🪛 GitHub Actions: Check style + docs + types
[error] 244-244: Ruff: Local variable
original_iter_modulesis assigned to but never used (F841). Remove assignment to unused variable.
| def parse(self, value: str): # Add type hint later if copying from parent | ||
| """ | ||
| Parse a Seleucid date string and return an :class:`~undate.undate.Undate` or | ||
| :class:`~undate.undate.UndateInterval`. | ||
| The Seleucid date string is preserved in the undate label. | ||
| This method overrides the parent to provide a Seleucid-specific error message. | ||
| """ | ||
| if not value: # Copied from HebrewDateConverter.parse | ||
| raise ValueError("Parsing empty string is not supported") | ||
|  | ||
| # Import the parser used by the parent class directly | ||
| from undate.converters.calendars.hebrew.parser import hebrew_parser | ||
| try: | ||
| # Uses the hebrew_parser and self.transformer (calendar set to SELEUCID in __init__) | ||
| parsetree = hebrew_parser.parse(value) | ||
| undate_obj = self.transformer.transform(parsetree) | ||
| undate_obj.label = f"{value} {self.calendar_name}" | ||
| return undate_obj | ||
| except Exception as err: # Catching broader Exception to be safe, can refine | ||
| # Check if it's an UnexpectedCharacters error if lark is used by hebrew_parser | ||
| from lark.exceptions import UnexpectedCharacters | ||
| if isinstance(err, UnexpectedCharacters): | ||
| raise ValueError(f"Could not parse '{value}' as a Seleucid date") from err | ||
| # Re-raise other errors or handle them if they are expected | ||
| raise # Re-raise if not the specific parsing error we want to customize | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Simplify error handling and improve code clarity.
The parse method logic is correct but overly complex. The current implementation catches broad exceptions and then checks their type, which is unnecessarily complicated.
-    def parse(self, value: str): # Add type hint later if copying from parent
+    def parse(self, value: str) -> Union[Undate, UndateInterval]:
         """
         Parse a Seleucid date string and return an :class:`~undate.undate.Undate` or
         :class:`~undate.undate.UndateInterval`.
         The Seleucid date string is preserved in the undate label.
-        This method overrides the parent to provide a Seleucid-specific error message.
         """
-        if not value: # Copied from HebrewDateConverter.parse
+        if not value:
             raise ValueError("Parsing empty string is not supported")
-        # Import the parser used by the parent class directly
         from undate.converters.calendars.hebrew.parser import hebrew_parser
+        from lark.exceptions import UnexpectedCharacters
+        
         try:
-            # Uses the hebrew_parser and self.transformer (calendar set to SELEUCID in __init__)
-            parsetree = hebrew_parser.parse(value) 
+            parsetree = hebrew_parser.parse(value)
             undate_obj = self.transformer.transform(parsetree)
             undate_obj.label = f"{value} {self.calendar_name}"
             return undate_obj
-        except Exception as err: # Catching broader Exception to be safe, can refine
-            # Check if it's an UnexpectedCharacters error if lark is used by hebrew_parser
-            from lark.exceptions import UnexpectedCharacters
-            if isinstance(err, UnexpectedCharacters):
-                 raise ValueError(f"Could not parse '{value}' as a Seleucid date") from err
-            # Re-raise other errors or handle them if they are expected
-            raise # Re-raise if not the specific parsing error we want to customize
+        except UnexpectedCharacters as err:
+            raise ValueError(f"Could not parse '{value}' as a Seleucid date") from errThe simplified version:
- Adds proper type hints
- Catches the specific exception we care about
- Removes unnecessary complexity
- Maintains the same functionality
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def parse(self, value: str): # Add type hint later if copying from parent | |
| """ | |
| Parse a Seleucid date string and return an :class:`~undate.undate.Undate` or | |
| :class:`~undate.undate.UndateInterval`. | |
| The Seleucid date string is preserved in the undate label. | |
| This method overrides the parent to provide a Seleucid-specific error message. | |
| """ | |
| if not value: # Copied from HebrewDateConverter.parse | |
| raise ValueError("Parsing empty string is not supported") | |
| # Import the parser used by the parent class directly | |
| from undate.converters.calendars.hebrew.parser import hebrew_parser | |
| try: | |
| # Uses the hebrew_parser and self.transformer (calendar set to SELEUCID in __init__) | |
| parsetree = hebrew_parser.parse(value) | |
| undate_obj = self.transformer.transform(parsetree) | |
| undate_obj.label = f"{value} {self.calendar_name}" | |
| return undate_obj | |
| except Exception as err: # Catching broader Exception to be safe, can refine | |
| # Check if it's an UnexpectedCharacters error if lark is used by hebrew_parser | |
| from lark.exceptions import UnexpectedCharacters | |
| if isinstance(err, UnexpectedCharacters): | |
| raise ValueError(f"Could not parse '{value}' as a Seleucid date") from err | |
| # Re-raise other errors or handle them if they are expected | |
| raise # Re-raise if not the specific parsing error we want to customize | |
| def parse(self, value: str) -> Union[Undate, UndateInterval]: | |
| """ | |
| Parse a Seleucid date string and return an :class:`~undate.undate.Undate` or | |
| :class:`~undate.undate.UndateInterval`. | |
| The Seleucid date string is preserved in the undate label. | |
| """ | |
| if not value: | |
| raise ValueError("Parsing empty string is not supported") | |
| from undate.converters.calendars.hebrew.parser import hebrew_parser | |
| from lark.exceptions import UnexpectedCharacters | |
| try: | |
| parsetree = hebrew_parser.parse(value) | |
| undate_obj = self.transformer.transform(parsetree) | |
| undate_obj.label = f"{value} {self.calendar_name}" | |
| return undate_obj | |
| except UnexpectedCharacters as err: | |
| raise ValueError(f"Could not parse '{value}' as a Seleucid date") from err | 
🤖 Prompt for AI Agents
In src/undate/converters/calendars/seleucid.py lines 47 to 71, simplify the
parse method by adding proper type hints and catching only the specific
exception (UnexpectedCharacters) instead of a broad Exception. Remove the
general try-except block and directly handle the specific parsing error to
improve clarity while preserving the existing functionality and error messaging.
        
          
                tests/test_converters/test_base.py
              
                Outdated
          
        
      | import undate.converters | ||
| converters_path = undate.converters.__path__ # This is a list of paths | ||
|  | ||
| original_iter_modules = importlib.import_module('pkgutil').iter_modules | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused variable assignment.
The original_iter_modules variable is assigned but never used.
-        original_iter_modules = importlib.import_module('pkgutil').iter_modules
-
         def simplified_mock_iter_modules(path=None, prefix=''):📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| original_iter_modules = importlib.import_module('pkgutil').iter_modules | |
| def simplified_mock_iter_modules(path=None, prefix=''): | 
🧰 Tools
🪛 Ruff (0.11.9)
244-244: Local variable original_iter_modules is assigned to but never used
Remove assignment to unused variable original_iter_modules
(F841)
🪛 GitHub Actions: Check style + docs + types
[error] 244-244: Ruff: Local variable original_iter_modules is assigned to but never used (F841). Remove assignment to unused variable.
🤖 Prompt for AI Agents
In tests/test_converters/test_base.py at line 244, remove the assignment of the
variable original_iter_modules since it is declared but never used. Simply
delete the entire line that assigns original_iter_modules to
importlib.import_module('pkgutil').iter_modules to clean up unused code.
| import importlib | ||
| from unittest.mock import patch, MagicMock | ||
| from typing import Type # For type hinting | ||
|  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import.
The Type import is not used in the code.
-from typing import Type # For type hinting📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| from typing import Type # For type hinting | 
🧰 Tools
🪛 Ruff (0.11.9)
5-5: typing.Type imported but unused
Remove unused import: typing.Type
(F401)
🪛 GitHub Actions: Check style + docs + types
[error] 5-5: Ruff: typing.Type imported but unused (F401). Remove unused import.
🤖 Prompt for AI Agents
In tests/test_converters/test_base.py at line 5, the import statement for `Type`
from the `typing` module is unused. Remove this import line entirely to clean up
the code and avoid unnecessary imports.
- Refined `SeleucidDateConverter.parse` method:
    - Added specific type hints for parameters and return values.
    - Ensured the try-except block specifically catches `lark.exceptions.UnexpectedCharacters`.
    - Preserved the existing error message format for parsing failures.
- Cleaned up `tests/test_converters/test_base.py`:
    - Removed an unused variable assignment (`original_iter_modules`).
    - Ensured an unused import (`from typing import Type`) was removed.
All relevant tests pass, confirming the changes are safe and improve code clarity.
    
…classes.
This commit introduces extensive unit tests for newly added and existing calendar functionality:
test_gregorian.py) to cover allBaseCalendarConvertermethods (min_month,max_month,max_day,to_gregorian, etc.).test_base.pyto verify thatavailable_converters()correctly discovers direct, nested, and calendar converter subclasses, and properly excludesBaseCalendarConverter. This included testing theimport_converterscaching and module loading.test_hebrew_converter.pywith explicit tests for allBaseCalendarConvertermethods. I made minor adjustments to the converter to handleNoneinputs for year/month inmax_dayandmax_month.test_islamic_converter.pywith explicit tests forBaseCalendarConvertermethods and corrected assertions intest_partially_known. I made minor adjustments to the converter to handleNoneinputs for year/month inmax_day.test_seleucid.pywith explicit tests forto_gregorian,max_month, andmax_dayusing Seleucid years. I overrodemax_month,max_day, andparsein the converter to correctly handle theSELEUCID_OFFSETand provide Seleucid-specific error messages. I also cleaned up commented code.All new and existing tests pass, ensuring better coverage and robustness of the date conversion and calendar logic.
Summary by CodeRabbit
New Features
Bug Fixes
Tests